#include "server.h"
struct FdInfo {
    int fd;
    int epfd;
    pthread_t tid;
};

int initListenFd(unsigned short port) {
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == lfd) {
        perror("socket error");
        return -1;
    }

    int opt = 1;
    if(-1 == setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt))) {
        perror("setsocketopt error");
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(-1 == bind(lfd, (struct sockaddr*)&addr, sizeof (addr))) {
        perror("bind error");
        return -1;
    }

    if(-1 == listen(lfd, 128)) {
        perror("liseten error");
        return -1;
    }
    return lfd;
}

int epollRun(int lfd) {
    int epfd = epoll_create(1);
    if(-1 == epfd) {
        perror("epoll_create");
        return -1;
    }

    struct epoll_event ev;
    ev.data.fd = lfd;
    ev.events = EPOLLIN;
    if(-1 == epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev)) {
        perror("epoll_ctl error");
        return -1;
    }

    struct epoll_event evs[1024];
    int size = sizeof(evs)/sizeof(struct epoll_event);
    while(true) {
        int num = epoll_wait(epfd, evs, size, -1);
        for(int i = 0; i < num; ++i) {
            int fd = evs[i].data.fd;
            if(fd == lfd) {
                acceptClient(lfd, epfd);
            } else {
                recvHttpRequest(fd, epfd);
            }
        }
    }
}
int acceptClient(int lfd, int epfd) {
    int cfd = accept(lfd, NULL, NULL);
    if(-1 == cfd) {
        perror("accept error");
        return -1;
    }

    int flag = fcntl(cfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd, F_SETFL, flag);

    struct epoll_event ev;
    ev.data.fd = cfd;
    ev.events = EPOLLIN | EPOLLET;
    if(-1 == epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev)) {
        perror("epoll_ctl error");
        return -1;
    }

    return 0;
}

//int recvHttpRequest(int cfd, int epfd)
void *recvHttpRequest(void* arg)
{
    struct FdInfo* info = (struct FdInfo*)arg;
    printf("开始接收数据了……\n");
    int len = 0, totle = 0;
    char buf[4096] = {0};
    char tmp[1024] = {0};
    while ((len = recv(info->fd, tmp, sizeof (tmp), 0)) > 0) {
        if((totle + len) < sizeof (buf)) {
            memcpy(buf+totle, tmp, len);
        }
        totle += len;
    }
    if(-1 == len && errno == EAGAIN) {
        char* pt = strstr(buf, "\r\n");
        int reqLen = pt - buf;
        buf[reqLen] = '\0';
    } else if (0 == len) {
        epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
        close(cfd);
    } else {
        perror("recv error");
    }
    return 0;
}

int parseRequestLine(const char *line, int cfd) {
    char method[12];
    char path[1024];
    sscanf(line, "%[^ ] %[^ ]", method, path);
    printf("method: %s, path: %s\n", method, path);
    if (strcasecmp(method, "get") != 0) {
        return -1;
    }
    decodeMsg(path, path);
    char * file = NULL;
    if(strcmp(path, "/") == 0) {
        file = "./";
    } else {
        file = path+1;
    }

    struct stat st;
    int ret = stat(file, &st);
    if(-1 == ret) {
        sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1);
    }

}

void decodeMsg(char* to, char* from)
{
    for (; *from != '\0'; ++to, ++from)
    {
        // isxdigit -> 判断字符是不是16进制格式, 取值在 0-f
        // Linux%E5%86%85%E6%A0%B8.jpg
        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2]))
        {
            // 将16进制的数 -> 十进制 将这个数值赋值给了字符 int -> char
            // B2 == 178
            // 将3个字符, 变成了一个字符, 这个字符就是原始数据
            *to = hexToDec(from[1]) * 16 + hexToDec(from[2]);

            // 跳过 from[1] 和 from[2] 因此在当前循环中已经处理过了
            from += 2;
        }
        else
        {
            // 字符拷贝, 赋值
            *to = *from;
        }
    }
    *to = '\0';
}

const char* getFileType(const char* name)
{
    // a.jpg a.mp4 a.html
    // 自右向左查找‘.’字符, 如不存在返回NULL
    const char* dot = strrchr(name, '.');
    if (dot == NULL)
        return "text/plain; charset=utf-8";	// 纯文本
    if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
        return "text/html; charset=utf-8";
    if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
        return "image/jpeg";
    if (strcmp(dot, ".gif") == 0)
        return "image/gif";
    if (strcmp(dot, ".png") == 0)
        return "image/png";
    if (strcmp(dot, ".css") == 0)
        return "text/css";
    if (strcmp(dot, ".au") == 0)
        return "audio/basic";
    if (strcmp(dot, ".wav") == 0)
        return "audio/wav";
    if (strcmp(dot, ".avi") == 0)
        return "video/x-msvideo";
    if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
        return "video/quicktime";
    if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
        return "video/mpeg";
    if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
        return "model/vrml";
    if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
        return "audio/midi";
    if (strcmp(dot, ".mp3") == 0)
        return "audio/mpeg";
    if (strcmp(dot, ".ogg") == 0)
        return "application/ogg";
    if (strcmp(dot, ".pac") == 0)
        return "application/x-ns-proxy-autoconfig";

    return "text/plain; charset=utf-8";
}

int sendHeadMsg(int cfd, int status, const char *descr, const char *type, int length) {
    char buf[4096] = {0};
    sprintf(buf, "http/1.1 %d %s\r\n", status, descr);

    sprintf(buf + strlen(buf), "content-type: %s\r\n", type);
    sprintf(buf + strlen(buf), "content-length: %d\r\n\r\n", length);

    send(cfd, buf, strlen(buf), 0);
    return 0;
}

int sendFile(const char *fileName, int cfd)
{
    int fd = open(fileName, O_RDONLY);
    assert(fd > 0);

    off_t offset = 0;
    int size = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);
    while(offset < size) {
        int ret = sendfile(cfd, fd, &offset, size - offset);
        printf("ret value: %d\n", ret);
        if(ret == -1 && errno == EA)
    }
}
